#include "MephistoCAM.h"

/*
//  The following 7 macros are 'opt's
#define _AMP_CTRA_      0
#define _AMP_CTRB_      1
#define _MONITOR_EN_    2
#define _44_CTL_        3
#define _CAP_CTRA_      4
#define _CAP_CTRB_      5
#define _OS_CTR_        6

typedef struct _ADG714_Config{
    int spi_tx_size;    // 一次SPI传输的数据bit数,对于ADG714而言是8bits
    int _AMP_CTRA;       // AMP_CTRA & AMP_CTRB 共同确定一级运放的放大倍数
    int _AMP_CTRB;       // 
    int _MONITOR_EN;     // 控制是否对驱动时序进行监测
    int _44_CTL;        // 控制是否采用44所CCD
    int _CAP_CTRA;       // CAP_CTRA & CAP_CTRB 共同确定滤波电容的大小
    int _CAP_CTRB;
    int _OS_CTR;         // 控制是否将OSx-S于OSx-D连接
} ADG714_Config;
*/

//  initialize ADG714_Config *adg714_cfg to default settings
int init_adg714_config( ADG714_Config *adg714_cfg ){

    adg714_cfg->spi_tx_size = 0;

    //  by default, TGA & TGD are connected to A4 & D1, respectively.
    adg714_cfg->_AMP_CTRA   = 0;
    adg714_cfg->_AMP_CTRB   = 0;
    
    adg714_cfg->_MONITOR_EN = 0;
    //  by default, pinning phase is NOT enabled, so we set it to 0
    adg714_cfg->_44_CTL     = 0;
    
    //  by default, 
    adg714_cfg->_CAP_CTRA   = 0;
    adg714_cfg->_CAP_CTRB   = 0;
    adg714_cfg->_OS_CTR     = 0;

#ifdef _DEBUG_ADG714_
    print_adg714_config( adg714_cfg );
#endif

    return 0;
}

//  read ADG714 configuration data from *inifile
int read_adg714_config( ADG714_Config *adg714_cfg, char *inifile, char *section ){

    dictionary *ini;
	ini = iniparser_load(inifile);
	
	char sec_param[64];
	
	if( ini == NULL ){
		fprintf(stderr, "cannot parse file: %s\n", inifile);
		return 0;
	} else {
	    sprintf(sec_param, "%s:spi_tx_size", section);
	    adg714_cfg->spi_tx_size = iniparser_getint(ini, "ADG714:spi_tx_size", 8);
	    
	    sprintf(sec_param, "%s:AMP_CTRA", section);
        adg714_cfg->_AMP_CTRA   = iniparser_getint(ini, "ADG714:AMP_CTRA", 0);
        
        sprintf(sec_param, "%s:AMP_CTRB", section);
        adg714_cfg->_AMP_CTRB   = iniparser_getint(ini, "ADG714:AMP_CTRB", 0);
        
        sprintf(sec_param, "%s:MONITOR_EN", section);
        adg714_cfg->_MONITOR_EN = iniparser_getint(ini, "ADG714:MONITOR_EN", 0);
        
        sprintf(sec_param, "%s:44_CTL", section);
        adg714_cfg->_44_CTL     = iniparser_getint(ini, "ADG714:44_CTL", 0);
        
        sprintf(sec_param, "%s:CAP_CTRA", section);
        adg714_cfg->_CAP_CTRA   = iniparser_getint(ini, "ADG714:CAP_CTRA", 0);
        
        sprintf(sec_param, "%s:CAP_CTRB", section);
        adg714_cfg->_CAP_CTRB   = iniparser_getint(ini, "ADG714:CAP_CTRB", 0);
        
        sprintf(sec_param, "%s:OS_CTR", section);
        adg714_cfg->_OS_CTR     = iniparser_getint(ini, "ADG714:OS_CTR", 0);    
    }

    // printf("\n> finished read params from: %s [section:%s]\n", inifile, section);

    iniparser_freedict( ini );
    
    return 0;
}

//  print ADG714 configurations for debug
int print_adg714_config( ADG714_Config *adg714_cfg ){
    printf("-----------------------------------------------\n");
    printf("(adg714 --> AMP_CTRA    = %d\n", adg714_cfg->_AMP_CTRA);
    printf("(adg714 --> AMP_CTRB    = %d\n", adg714_cfg->_AMP_CTRB);
    printf("(adg714 --> MONITOR_EN  = %d\n", adg714_cfg->_MONITOR_EN);
    printf("(adg714 --> 44_CTL      = %d\n", adg714_cfg->_44_CTL);
    printf("(adg714 --> CAP_CTRA    = %d\n", adg714_cfg->_CAP_CTRA);
    printf("(adg714 --> CAP_CTRB    = %d\n", adg714_cfg->_CAP_CTRB);
    printf("(adg714 --> OS_CTR      = %d\n", adg714_cfg->_OS_CTR);
    printf("-----------------------------------------------\n");
    return 0;
}

// duplicate ADG714_Config
int duplicate_ADG714( ADG714_Config *cfg_old, ADG714_Config *cfg_new ){
    
    cfg_new->spi_tx_size    = cfg_old->spi_tx_size;
    cfg_new->_AMP_CTRA      = cfg_old->_AMP_CTRA;
    cfg_new->_AMP_CTRB      = cfg_old->_AMP_CTRB;
    cfg_new->_MONITOR_EN    = cfg_old->_MONITOR_EN;
    cfg_new->_44_CTL        = cfg_old->_44_CTL;
    cfg_new->_CAP_CTRA      = cfg_old->_CAP_CTRB;
    cfg_new->_OS_CTR        = cfg_old->_OS_CTR;

    return 0;
}

// 将从ini参数文件读取的参数组织成24bits的spi数据
uint32_t get_adg714_reg_data( ADG714_Config *cfg ){

    uint32_t reg_data = 0;
    uint32_t reg_data_24bits = 0;
    
    reg_data |= (cfg->_OS_CTR       << 6);
    reg_data |= (cfg->_CAP_CTRB     << 5);
    reg_data |= (cfg->_CAP_CTRA     << 4);
    reg_data |= (cfg->_44_CTL       << 3);
    reg_data |= (cfg->_MONITOR_EN   << 2);
    reg_data |= (cfg->_AMP_CTRB     << 1);
    reg_data |= (cfg->_AMP_CTRA     << 0);
    
    reg_data_24bits |= (reg_data << 16);
    reg_data_24bits |= (reg_data << 8);
    reg_data_24bits |= reg_data;

#if defined (_DEBUG_ADG714_)
    print_as_bin( reg_data_24bits );
#endif
    return reg_data_24bits;
}

//  update configuration for a 'opt' with value 'pValue'
int update_adg714_config( ADG714_Config *adg714_cfg, char* pName, int pValue ){

    int pName_opt = -1;
    
    if( strcmp(pName, "AMP_A") == 0 || strcmp(pName, "amp_a") == 0 )
        pName_opt = _AMP_CTRA_;
    else if( strcmp(pName, "AMP_B") == 0 || strcmp(pName, "amp_b") == 0)
        pName_opt = _AMP_CTRB_;
    else if( strcmp(pName, "MONITOR") == 0 || strcmp(pName, "monitor") == 0)
        pName_opt = _MONITOR_EN_;
    else if( strcmp(pName, "44S") == 0 || strcmp(pName, "44s") == 0)
        pName_opt = _44_CTL_;
    else if( strcmp(pName, "CAP_A") == 0 || strcmp(pName, "cap_a") == 0)
        pName_opt = _CAP_CTRA_;
    else if( strcmp(pName, "CAP_B") == 0 || strcmp(pName, "cap_b") == 0)
        pName_opt = _CAP_CTRB_;
    else if( strcmp(pName, "OS") == 0 || strcmp(pName, "os") == 0)
        pName_opt = _OS_CTR_;

    switch( pName_opt ) {
        case _AMP_CTRA_:  adg714_cfg->_AMP_CTRA = pValue;
                    printf("> updating ADG714->AMP_CTRA to %d\n", pValue);
                    break;
                    
        case _AMP_CTRB_:  adg714_cfg->_AMP_CTRB = pValue;
                    printf("> updating ADG714->AMP_CTRB to %d\n", pValue);
                    break;

        case _MONITOR_EN_: adg714_cfg->_MONITOR_EN = pValue;
                    printf("> updating ADG714->MONITOR_EN to %d\n", pValue);
                    break;
                    
        case _44_CTL_: adg714_cfg->_44_CTL = pValue;
                    printf("> updating ADG714->_44_CTL to %d\n", pValue);
                    break;
                    
        case _CAP_CTRA_:  adg714_cfg->_CAP_CTRA = pValue;
                    printf("> updating ADG714->CAP_CTRA to %d\n", pValue);
                    break;
                    
        case _CAP_CTRB_:  adg714_cfg->_CAP_CTRB = pValue;
                    printf("> updating ADG714->CAP_CTRB to %d\n", pValue);
                    break;
                    
        case _OS_CTR_:  adg714_cfg->_OS_CTR = pValue;
                    printf("> updating ADG714->OS_CTR to %d\n", pValue);
                    break;
        
        default:    printDebugInfo("No legeal opt is given!");
                    printf("> Legeal opts are:\n");
                    printf("> \tAMP_A (amp_a)\n");
                    printf("> \tAMP_B (amp_b)\n");
                    printf("> \tMONITOR (monitor)\n");
                    printf("> \t44S (44s)\n");
                    printf("> \tCAP_A (cap_a)\n");
                    printf("> \tCAP_B (cap_b)\n");
                    printf("> \tOS (os)\n");
    }

    return 0;
}

//  send configuration via SPI
//  MUST be called after either 'load_adg714_config(***)' or 
//  'update_adg714_config(***)' were called
int config_adg714_via_SPI(  uint32_t *gpio_reg, 
					        uint32_t *gpio2_reg, 
					        uint32_t *gpio_in_reg,
					        SPI_CONFIG *spi_config,
					        ADG714_Config *adg714_cfg ){
    
    // prepare SPI data to be transferred
    // 0000_0000_'0'0'S6'S5'_S4'S3'S2'S1
    uint32_t data = 0;
    data |= ( adg714_cfg->_AMP_CTRA     << 0 );
    data |= ( adg714_cfg->_AMP_CTRB     << 1 );
    data |= ( adg714_cfg->_MONITOR_EN   << 2 );
    data |= ( adg714_cfg->_44_CTL       << 3 );
    data |= ( adg714_cfg->_CAP_CTRA     << 4 );
    data |= ( adg714_cfg->_CAP_CTRB     << 5 );
    data |= ( adg714_cfg->_OS_CTR       << 6 );

    SPI_DATA *spi_data = (SPI_DATA*)malloc(sizeof(SPI_DATA));
    
    spi_data_alloc(spi_data, 1);
    
    //  addr is not needed, so we set it to 0
    spi_data_set(   spi_data, 
                    0, 
                    0,  // address = 0, so left shift has no effect to reg_addr_val
                    data );
                    
    // send SPI data to ADG714
    send_spi_data(  gpio_reg,
                    gpio2_reg,
                    gpio_in_reg,
                    spi_config, 
                    spi_data );
                    
    // finish SPI comm., free allocated memory
    spi_data_free(spi_data);
    
    return 0;
}
